{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kDn_lVxg3Z2G"
      },
      "source": [
        "# Importing a library that is not in Colaboratory\n",
        "\n",
        "To import a library that's not in Colaboratory by default, you can use `!pip install` or `!apt-get install`."
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install pycryptodome\n"
      ],
      "metadata": {
        "id": "rZQtmX1BBgWA",
        "outputId": "3ec789ca-1546-42a3-9c63-5888d784760a",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Collecting pycryptodome\n",
            "  Downloading pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.4 kB)\n",
            "Downloading pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.1 MB)\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.1/2.1 MB\u001b[0m \u001b[31m9.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25hInstalling collected packages: pycryptodome\n",
            "Successfully installed pycryptodome-3.20.0\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "import random\n",
        "import csv\n",
        "from Crypto.Cipher import AES, Blowfish, DES3, DES, ARC4\n",
        "from Crypto.Util.Padding import pad\n",
        "from Crypto.Random import get_random_bytes\n",
        "\n",
        "# Function to generate random plaintexts\n",
        "def generate_plaintexts(num, size):\n",
        "    return [get_random_bytes(size) for _ in range(num)]\n",
        "\n",
        "# Encryption functions for each algorithm\n",
        "def aes_encrypt(plaintext, key):\n",
        "    cipher = AES.new(key, AES.MODE_ECB)\n",
        "    return cipher.encrypt(pad(plaintext, AES.block_size))\n",
        "\n",
        "def blowfish_encrypt(plaintext, key):\n",
        "    cipher = Blowfish.new(key, Blowfish.MODE_ECB)\n",
        "    return cipher.encrypt(pad(plaintext, Blowfish.block_size))\n",
        "\n",
        "def des3_encrypt(plaintext, key):\n",
        "    cipher = DES3.new(key, DES3.MODE_ECB)\n",
        "    return cipher.encrypt(pad(plaintext, DES3.block_size))\n",
        "\n",
        "def des_encrypt(plaintext, key):\n",
        "    cipher = DES.new(key, DES.MODE_ECB)\n",
        "    return cipher.encrypt(pad(plaintext, DES.block_size))\n",
        "\n",
        "def rc4_encrypt(plaintext, key):\n",
        "    cipher = ARC4.new(key)  # Using ARC4 for RC4 encryption\n",
        "    return cipher.encrypt(plaintext)\n",
        "\n",
        "# Function to save ciphertexts to a CSV file (single file for all algorithms)\n",
        "def save_to_csv(filename, data):\n",
        "    with open(filename, mode='w', newline='') as file:\n",
        "        writer = csv.writer(file)\n",
        "        writer.writerow(['Algorithm', 'Ciphertext'])  # Add headers\n",
        "\n",
        "        for row in data:\n",
        "            writer.writerow(row)\n",
        "\n",
        "# Generate 220 plaintexts of size 16 bytes\n",
        "num_plaintexts = 220\n",
        "plaintext_size = 16\n",
        "plaintexts = generate_plaintexts(num_plaintexts, plaintext_size)\n",
        "\n",
        "# Generate a common key for each algorithm\n",
        "aes_key = get_random_bytes(16)\n",
        "blowfish_key = get_random_bytes(16)\n",
        "des3_key = DES3.adjust_key_parity(get_random_bytes(24))  # DES3 needs 24 bytes for 3DES\n",
        "des_key = get_random_bytes(8)  # DES key is 8 bytes\n",
        "rc4_key = get_random_bytes(16)  # RC4 key\n",
        "\n",
        "# Dictionary to hold ciphertexts for each algorithm\n",
        "ciphertexts = {\n",
        "    'AES': [],\n",
        "    'Blowfish': [],\n",
        "    '3DES': [],\n",
        "    'DES': [],\n",
        "    'RC4': []\n",
        "}\n",
        "\n",
        "# Encrypt all plaintexts with each algorithm using the same keys\n",
        "for pt in plaintexts:\n",
        "    ciphertexts['AES'].append(aes_encrypt(pt, aes_key))\n",
        "    ciphertexts['Blowfish'].append(blowfish_encrypt(pt, blowfish_key))\n",
        "    ciphertexts['3DES'].append(des3_encrypt(pt, des3_key))\n",
        "    ciphertexts['DES'].append(des_encrypt(pt, des_key))\n",
        "    ciphertexts['RC4'].append(rc4_encrypt(pt, rc4_key))\n",
        "\n",
        "# Prepare a list of data to write to a single CSV file\n",
        "csv_data = []\n",
        "\n",
        "for algo, cts in ciphertexts.items():\n",
        "    for ct in cts:\n",
        "        csv_data.append([algo, ct.hex()])  # Convert binary to hex for readable format\n",
        "\n",
        "# Save everything to a single CSV file\n",
        "save_to_csv('all_ciphertexts.csv', csv_data)\n",
        "print(\"All data saved to 'all_ciphertexts.csv'\")\n",
        "\n"
      ],
      "metadata": {
        "id": "21sAOYj_DlyB",
        "outputId": "36e3e192-4d7c-491d-dfea-182fc3d32fc2",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 3,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "All data saved to 'all_ciphertexts.csv'\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Check the number of rows in the CSV file\n",
        "import pandas as pd\n",
        "df = pd.read_csv('all_ciphertexts.csv')\n",
        "print(\"Total ciphertexts:\", len(df))\n"
      ],
      "metadata": {
        "id": "EVka758lO7Fa",
        "outputId": "92c5940f-d01f-42e4-842e-9d19d7a6eb91",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 8,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Total ciphertexts: 1100\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import pandas as pd\n",
        "from sklearn.utils import shuffle\n",
        "\n",
        "# Function to load ciphertexts from CSV file\n",
        "def load_ciphertexts_from_csv(filename):\n",
        "    df = pd.read_csv(filename)\n",
        "    return df\n",
        "\n",
        "# Function to save training and testing data to separate CSV files\n",
        "def save_training_testing_data(training_data, testing_data, training_filename, testing_filename):\n",
        "    training_data.to_csv(training_filename, index=False)\n",
        "    testing_data.to_csv(testing_filename, index=False)\n",
        "\n",
        "# Load ciphertexts from the CSV file\n",
        "df = load_ciphertexts_from_csv('all_ciphertexts.csv')\n",
        "\n",
        "# Shuffle the dataframe to ensure randomness\n",
        "df = shuffle(df, random_state=42)\n",
        "\n",
        "# Debugging: Check total number of samples\n",
        "total_samples = len(df)\n",
        "print(f\"Total samples in data: {total_samples}\")\n",
        "\n",
        "# Separate the data into training and testing sets\n",
        "num_training_samples = 40 * 5  # 40 ciphertexts per algorithm\n",
        "num_testing_samples = 180  # Total 180 ciphertexts for testing (9 groups of 20)\n",
        "\n",
        "# Split into training and testing\n",
        "df_training = df.groupby('Algorithm').head(40).reset_index(drop=True)\n",
        "df_testing = df.drop(df_training.index).reset_index(drop=True)\n",
        "\n",
        "# Debugging: Check sizes of the training and testing sets\n",
        "print(f\"Number of training samples: {len(df_training)}\")\n",
        "print(f\"Number of testing samples: {len(df_testing)}\")\n",
        "\n",
        "# Ensure that the number of testing samples is exactly 180 (9 groups of 20)\n",
        "if len(df_testing) != num_testing_samples:\n",
        "    print(f\"Warning: Expected {num_testing_samples} testing samples, but found {len(df_testing)}.\")\n",
        "\n",
        "# Save training and testing data to CSV files\n",
        "save_training_testing_data(df_training, df_testing, 'training_ciphertexts.csv', 'testing_ciphertexts.csv')\n",
        "\n",
        "print(\"Training and testing data saved to 'training_ciphertexts.csv' and 'testing_ciphertexts.csv'\")\n",
        "\n",
        "# Optional: Dividing testing data into 9 groups of 20\n",
        "def divide_testing_data(testing_df, num_groups, group_size):\n",
        "    groups = []\n",
        "    for i in range(num_groups):\n",
        "        start_idx = i * group_size\n",
        "        end_idx = start_idx + group_size\n",
        "        groups.append(testing_df[start_idx:end_idx])\n",
        "    return groups\n",
        "\n",
        "# Divide the testing data into 9 groups of 20\n",
        "groups = divide_testing_data(df_testing, 9, 20)\n",
        "\n",
        "# Save each group to separate CSV files\n",
        "for i, group in enumerate(groups):\n",
        "    group.to_csv(f'testing_group_{i+1}.csv', index=False)\n",
        "    print(f\"Testing group {i+1} saved to 'testing_group_{i+1}.csv'\")\n"
      ],
      "metadata": {
        "id": "V1oFXPcfQKwG",
        "outputId": "e886f345-9d8d-4109-93cc-beead5bb01c2",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 9,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Total samples in data: 1100\n",
            "Number of training samples: 200\n",
            "Number of testing samples: 900\n",
            "Warning: Expected 180 testing samples, but found 900.\n",
            "Training and testing data saved to 'training_ciphertexts.csv' and 'testing_ciphertexts.csv'\n",
            "Testing group 1 saved to 'testing_group_1.csv'\n",
            "Testing group 2 saved to 'testing_group_2.csv'\n",
            "Testing group 3 saved to 'testing_group_3.csv'\n",
            "Testing group 4 saved to 'testing_group_4.csv'\n",
            "Testing group 5 saved to 'testing_group_5.csv'\n",
            "Testing group 6 saved to 'testing_group_6.csv'\n",
            "Testing group 7 saved to 'testing_group_7.csv'\n",
            "Testing group 8 saved to 'testing_group_8.csv'\n",
            "Testing group 9 saved to 'testing_group_9.csv'\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Confirm the number of samples per algorithm in the original data\n",
        "for algo in ['AES', 'Blowfish', '3DES', 'DES', 'RC4']:\n",
        "    num_samples_per_algo = len(df[df['Algorithm'] == algo])\n",
        "    print(f\"Number of samples for {algo}: {num_samples_per_algo}\")\n"
      ],
      "metadata": {
        "id": "6JP_gi2zQch2",
        "outputId": "fca2808b-1266-440d-db68-ba3d72ba360b",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 10,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Number of samples for AES: 220\n",
            "Number of samples for Blowfish: 220\n",
            "Number of samples for 3DES: 220\n",
            "Number of samples for DES: 220\n",
            "Number of samples for RC4: 220\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "from scipy.stats import chisquare\n",
        "from collections import Counter\n",
        "\n",
        "# English letter frequencies (relative frequencies)\n",
        "english_freq = {\n",
        "    'a': 0.08167, 'b': 0.01492, 'c': 0.02782, 'd': 0.04253,\n",
        "    'e': 0.12702, 'f': 0.02228, 'g': 0.02015, 'h': 0.06094,\n",
        "    'i': 0.06966, 'j': 0.00153, 'k': 0.00772, 'l': 0.04025,\n",
        "    'm': 0.02406, 'n': 0.06749, 'o': 0.07507, 'p': 0.01929,\n",
        "    'q': 0.00095, 'r': 0.05987, 's': 0.06327, 't': 0.09056,\n",
        "    'u': 0.02758, 'v': 0.00978, 'w': 0.02360, 'x': 0.00150,\n",
        "    'y': 0.01974, 'z': 0.00074\n",
        "}\n",
        "\n",
        "# Load ciphertexts from CSV file\n",
        "def load_ciphertexts_from_csv(filename):\n",
        "    df = pd.read_csv(filename)\n",
        "    return df\n",
        "\n",
        "# Frequency distribution of characters\n",
        "def frequency_distribution(text):\n",
        "    text = text.lower()\n",
        "    freq = Counter(text)\n",
        "    total = sum(freq.values())\n",
        "    return {char: count / total for char, count in freq.items()}\n",
        "\n",
        "# Chi-squared statistic\n",
        "def chi_squared_statistic(freq_dist):\n",
        "    # Filter out English letters that are not in the text\n",
        "    filtered_english_freq = {char: freq for char, freq in english_freq.items() if char in freq_dist}\n",
        "    observed = np.array([freq_dist.get(char, 0) for char in filtered_english_freq])\n",
        "    expected = np.array([filtered_english_freq[char] for char in filtered_english_freq])\n",
        "\n",
        "    # Check if both distributions are non-empty\n",
        "    if observed.sum() == 0 or expected.sum() == 0:\n",
        "        return float('nan')  # Return NaN if the data is not valid\n",
        "\n",
        "    # Normalize observed and expected frequencies\n",
        "    observed = observed / observed.sum()\n",
        "    expected = expected / expected.sum()\n",
        "\n",
        "    return chisquare(observed, expected)[0]\n",
        "\n",
        "# Index of Coincidence\n",
        "def index_of_coincidence(text):\n",
        "    text = text.lower()\n",
        "    length = len(text)\n",
        "    freq = Counter(text)\n",
        "    return sum(count * (count - 1) for count in freq.values()) / (length * (length - 1))\n",
        "\n",
        "# Max IC for periods 1-15\n",
        "def max_ic_for_periods(ciphertext, max_period=15):\n",
        "    def split_text(text, period):\n",
        "        return [text[i::period] for i in range(period)]\n",
        "\n",
        "    def avg_ic(text_list):\n",
        "        return np.mean([index_of_coincidence(text) for text in text_list])\n",
        "\n",
        "    ics = []\n",
        "    for period in range(1, max_period + 1):\n",
        "        split_texts = split_text(ciphertext, period)\n",
        "        ics.append(avg_ic(split_texts))\n",
        "    return max(ics)\n",
        "\n",
        "# Max Kappa for periods 1-15\n",
        "def max_kappa_for_periods(ciphertext, max_period=15):\n",
        "    def kappa(text, period):\n",
        "        shifted_text = text[period:] + text[:period]\n",
        "        return np.mean([text[i] == shifted_text[i] for i in range(len(text))])\n",
        "\n",
        "    kappa_values = []\n",
        "    for period in range(1, max_period + 1):\n",
        "        kappa_values.append(kappa(ciphertext, period))\n",
        "    return max(kappa_values)\n",
        "\n",
        "# Digraphic Index of Coincidence\n",
        "def digraphic_index_of_coincidence(text):\n",
        "    pairs = [text[i:i+2] for i in range(len(text) - 1)]\n",
        "    freq = Counter(pairs)\n",
        "    total = sum(freq.values())\n",
        "    return sum(count * (count - 1) for count in freq.values()) / (total * (total - 1))\n",
        "\n",
        "# DIC for even-numbered pairs\n",
        "def dic_for_even_numbered_pairs(text):\n",
        "    pairs = [text[i:i+2] for i in range(0, len(text) - 1, 2)]\n",
        "    freq = Counter(pairs)\n",
        "    total = sum(freq.values())\n",
        "    return sum(count * (count - 1) for count in freq.values()) / (total * (total - 1))\n",
        "\n",
        "# Long repeat\n",
        "def long_repeat(text):\n",
        "    repeats = [text[i:i+3] for i in range(len(text) - 2)]\n",
        "    freq = Counter(repeats)\n",
        "    long_repeats = [repeat for repeat, count in freq.items() if count > 1]\n",
        "    return np.sqrt(len(long_repeats) / len(repeats)) if repeats else 0\n",
        "\n",
        "# Percentage of odd-spaced repeats\n",
        "def percentage_of_odd_spaced_repeats(text):\n",
        "    repeats = [text[i:i+3] for i in range(len(text) - 2)]\n",
        "    odd_spaced_repeats = [repeat for i, repeat in enumerate(repeats) if repeat in repeats[:i]]\n",
        "    return len(odd_spaced_repeats) / len(repeats) * 100 if repeats else 0\n",
        "\n",
        "# Log digraph score (example placeholder data)\n",
        "def log_digraph_score(text, log_digraph_scores):\n",
        "    pairs = [text[i:i+2] for i in range(len(text) - 1)]\n",
        "    total_score = sum(log_digraph_scores.get(pair, 0) for pair in pairs)\n",
        "    return total_score / len(pairs) if pairs else 0\n",
        "\n",
        "# Single letter-digraph discrepancy score (example placeholder data)\n",
        "def single_letter_digraph_discrepancy(text, single_letter_scores, digraph_scores):\n",
        "    single_scores = [single_letter_scores.get(char, 0) for char in text]\n",
        "    digraph_scores_list = [digraph_scores.get(text[i:i+2], 0) for i in range(len(text) - 1)]\n",
        "    return np.mean(single_scores) + np.mean(digraph_scores_list) if text else 0\n",
        "\n",
        "# Load ciphertexts from the CSV file\n",
        "df = load_ciphertexts_from_csv('training_ciphertexts.csv')\n",
        "\n",
        "# Define log digraph scores and single letter scores (example data)\n",
        "log_digraph_scores = {'ex': 0.1, 'xa': 0.05}  # Example data\n",
        "single_letter_scores = {'e': 0.2, 'x': 0.1}  # Example data\n",
        "digraph_scores = {'ex': 0.15, 'xa': 0.1}  # Example data\n",
        "\n",
        "# Extract features for each ciphertext\n",
        "features = []\n",
        "for index, row in df.iterrows():\n",
        "    ciphertext = row['Ciphertext']  # Adjust column name if necessary\n",
        "    features.append({\n",
        "        'Algorithm': row['Algorithm'],  # Include algorithm for reference\n",
        "        'Number of Unique Characters': len(set(ciphertext)),\n",
        "        'Chi-Squared Statistic': chi_squared_statistic(frequency_distribution(ciphertext)),\n",
        "        'Index of Coincidence': index_of_coincidence(ciphertext),\n",
        "        'Max IC for Periods 1-15': max_ic_for_periods(ciphertext),\n",
        "        'Max Kappa for Periods 1-15': max_kappa_for_periods(ciphertext),\n",
        "        'Digraphic Index of Coincidence': digraphic_index_of_coincidence(ciphertext),\n",
        "        'DIC for Even-Numbered Pairs': dic_for_even_numbered_pairs(ciphertext),\n",
        "        'Long Repeat': long_repeat(ciphertext),\n",
        "        'Percentage of Odd-Spaced Repeats': percentage_of_odd_spaced_repeats(ciphertext),\n",
        "        'Log Digraph Score': log_digraph_score(ciphertext, log_digraph_scores),\n",
        "        'Single Letter-Digraph Discrepancy Score': single_letter_digraph_discrepancy(ciphertext, single_letter_scores, digraph_scores)\n",
        "    })\n",
        "\n",
        "# Convert features to DataFrame and save to CSV\n",
        "features_df = pd.DataFrame(features)\n",
        "features_df.to_csv('ciphertext_features.csv', index=False)\n",
        "\n",
        "print(\"Feature extraction complete. Results saved to 'ciphertext_features.csv'\")\n"
      ],
      "metadata": {
        "id": "Qk170gWdQnXc",
        "outputId": "221b70ed-0b21-43e7-acd2-0d779ec394df",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 11,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Feature extraction complete. Results saved to 'ciphertext_features.csv'\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import pandas as pd\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.svm import SVC\n",
        "from sklearn.ensemble import RandomForestClassifier\n",
        "from sklearn.metrics import classification_report, confusion_matrix\n",
        "import joblib\n",
        "\n",
        "# Load features from CSV file\n",
        "def load_features_from_csv(filename):\n",
        "    return pd.read_csv(filename)\n",
        "\n",
        "# Load features\n",
        "features_df = load_features_from_csv('ciphertext_features.csv')\n",
        "\n",
        "# Separate features and labels\n",
        "X = features_df.drop(columns=['Algorithm'])\n",
        "y = features_df['Algorithm']\n",
        "\n",
        "# Encode categorical labels as integers\n",
        "y_encoded = y.astype('category').cat.codes\n",
        "\n",
        "# Split data into training and testing sets\n",
        "X_train, X_test, y_train, y_test = train_test_split(X, y_encoded, test_size=0.3, random_state=42)\n",
        "\n",
        "# Initialize and train SVM classifier\n",
        "svm_classifier = SVC(kernel='linear')  # You can use other kernels like 'rbf' or 'poly'\n",
        "svm_classifier.fit(X_train, y_train)\n",
        "\n",
        "# Predict on test set using SVM\n",
        "y_pred_svm = svm_classifier.predict(X_test)\n",
        "\n",
        "# Evaluate the SVM model\n",
        "print(\"Confusion Matrix for SVM:\")\n",
        "print(confusion_matrix(y_test, y_pred_svm))\n",
        "\n",
        "print(\"\\nClassification Report for SVM:\")\n",
        "print(classification_report(y_test, y_pred_svm, target_names=y.unique()))\n",
        "\n",
        "# Initialize and train Random Forest classifier\n",
        "rf_classifier = RandomForestClassifier(n_estimators=100, random_state=42)\n",
        "rf_classifier.fit(X_train, y_train)\n",
        "\n",
        "# Predict on test set using Random Forest\n",
        "y_pred_rf = rf_classifier.predict(X_test)\n",
        "\n",
        "# Evaluate the Random Forest model\n",
        "print(\"\\nConfusion Matrix for Random Forest:\")\n",
        "print(confusion_matrix(y_test, y_pred_rf))\n",
        "\n",
        "print(\"\\nClassification Report for Random Forest:\")\n",
        "print(classification_report(y_test, y_pred_rf, target_names=y.unique()))\n",
        "\n",
        "# Feature importance from Random Forest\n",
        "feature_importances = rf_classifier.feature_importances_\n",
        "features = X.columns\n",
        "\n",
        "# Print the feature importances\n",
        "print(\"\\nFeature Importances from Random Forest:\")\n",
        "for feature, importance in zip(features, feature_importances):\n",
        "    print(f\"{feature}: {importance}\")\n",
        "\n",
        "# Optionally, save the trained models\n",
        "joblib.dump(svm_classifier, 'svm_classifier_model.joblib')\n",
        "joblib.dump(rf_classifier, 'rf_classifier_model.joblib')\n",
        "print(\"\\nModels saved to 'svm_classifier_model.joblib' and 'rf_classifier_model.joblib'\")\n"
      ],
      "metadata": {
        "id": "whOGss8tTjY-",
        "outputId": "e859290d-abfc-494b-92d1-3b3236c11196",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Confusion Matrix for SVM:\n",
            "[[ 3  2  2  6  1]\n",
            " [ 2 10  0  0  0]\n",
            " [ 2  2  3  3  1]\n",
            " [ 2  4  0  4  3]\n",
            " [ 0  0  0  0 10]]\n",
            "\n",
            "Classification Report for SVM:\n",
            "              precision    recall  f1-score   support\n",
            "\n",
            "    Blowfish       0.33      0.21      0.26        14\n",
            "         DES       0.56      0.83      0.67        12\n",
            "         AES       0.60      0.27      0.37        11\n",
            "        3DES       0.31      0.31      0.31        13\n",
            "         RC4       0.67      1.00      0.80        10\n",
            "\n",
            "    accuracy                           0.50        60\n",
            "   macro avg       0.49      0.53      0.48        60\n",
            "weighted avg       0.48      0.50      0.46        60\n",
            "\n",
            "\n",
            "Confusion Matrix for Random Forest:\n",
            "[[ 7  1  2  3  1]\n",
            " [ 1 10  1  0  0]\n",
            " [ 2  3  6  0  0]\n",
            " [ 4  1  2  3  3]\n",
            " [ 0  0  0  0 10]]\n",
            "\n",
            "Classification Report for Random Forest:\n",
            "              precision    recall  f1-score   support\n",
            "\n",
            "    Blowfish       0.50      0.50      0.50        14\n",
            "         DES       0.67      0.83      0.74        12\n",
            "         AES       0.55      0.55      0.55        11\n",
            "        3DES       0.50      0.23      0.32        13\n",
            "         RC4       0.71      1.00      0.83        10\n",
            "\n",
            "    accuracy                           0.60        60\n",
            "   macro avg       0.59      0.62      0.59        60\n",
            "weighted avg       0.58      0.60      0.57        60\n",
            "\n",
            "\n",
            "Feature Importances from Random Forest:\n",
            "Number of Unique Characters: 0.11606368426610886\n",
            "Chi-Squared Statistic: 0.11906037557019439\n",
            "Index of Coincidence: 0.1446666130439585\n",
            "Max IC for Periods 1-15: 0.20446965746059714\n",
            "Max Kappa for Periods 1-15: 0.09814541152363922\n",
            "Digraphic Index of Coincidence: 0.10630905206581327\n",
            "DIC for Even-Numbered Pairs: 0.061882064872516296\n",
            "Long Repeat: 0.012532150944111878\n",
            "Percentage of Odd-Spaced Repeats: 0.012809993339639903\n",
            "Log Digraph Score: 0.0\n",
            "Single Letter-Digraph Discrepancy Score: 0.12406099691342035\n",
            "\n",
            "Models saved to 'svm_classifier_model.joblib' and 'rf_classifier_model.joblib'\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "from scipy.stats import chisquare\n",
        "from collections import Counter\n",
        "\n",
        "# English letter frequencies (relative frequencies)\n",
        "english_freq = {\n",
        "    'a': 0.08167, 'b': 0.01492, 'c': 0.02782, 'd': 0.04253,\n",
        "    'e': 0.12702, 'f': 0.02228, 'g': 0.02015, 'h': 0.06094,\n",
        "    'i': 0.06966, 'j': 0.00153, 'k': 0.00772, 'l': 0.04025,\n",
        "    'm': 0.02406, 'n': 0.06749, 'o': 0.07507, 'p': 0.01929,\n",
        "    'q': 0.00095, 'r': 0.05987, 's': 0.06327, 't': 0.09056,\n",
        "    'u': 0.02758, 'v': 0.00978, 'w': 0.02360, 'x': 0.00150,\n",
        "    'y': 0.01974, 'z': 0.00074\n",
        "}\n",
        "\n",
        "# Load ciphertexts from CSV file\n",
        "def load_ciphertexts_from_csv(filename):\n",
        "    return pd.read_csv(filename)\n",
        "\n",
        "# Frequency distribution of characters\n",
        "def frequency_distribution(text):\n",
        "    text = text.lower()\n",
        "    freq = Counter(text)\n",
        "    total = sum(freq.values())\n",
        "    return {char: count / total for char, count in freq.items()}\n",
        "\n",
        "# Chi-squared statistic\n",
        "def chi_squared_statistic(freq_dist):\n",
        "    # Filter out English letters that are not in the text\n",
        "    filtered_english_freq = {char: freq for char, freq in english_freq.items() if char in freq_dist}\n",
        "    observed = np.array([freq_dist.get(char, 0) for char in filtered_english_freq])\n",
        "    expected = np.array([filtered_english_freq[char] for char in filtered_english_freq])\n",
        "\n",
        "    # Check if both distributions are non-empty\n",
        "    if observed.sum() == 0 or expected.sum() == 0:\n",
        "        return float('nan')  # Return NaN if the data is not valid\n",
        "\n",
        "    # Normalize observed and expected frequencies\n",
        "    observed = observed / observed.sum()\n",
        "    expected = expected / expected.sum()\n",
        "\n",
        "    return chisquare(observed, expected)[0]\n",
        "\n",
        "# Index of Coincidence\n",
        "def index_of_coincidence(text):\n",
        "    text = text.lower()\n",
        "    length = len(text)\n",
        "    freq = Counter(text)\n",
        "    return sum(count * (count - 1) for count in freq.values()) / (length * (length - 1))\n",
        "\n",
        "# Max IC for periods 1-15\n",
        "def max_ic_for_periods(ciphertext, max_period=15):\n",
        "    def split_text(text, period):\n",
        "        return [text[i::period] for i in range(period)]\n",
        "\n",
        "    def avg_ic(text_list):\n",
        "        return np.mean([index_of_coincidence(text) for text in text_list])\n",
        "\n",
        "    ics = []\n",
        "    for period in range(1, max_period + 1):\n",
        "        split_texts = split_text(ciphertext, period)\n",
        "        ics.append(avg_ic(split_texts))\n",
        "    return max(ics)\n",
        "\n",
        "# Max Kappa for periods 1-15\n",
        "def max_kappa_for_periods(ciphertext, max_period=15):\n",
        "    def kappa(text, period):\n",
        "        shifted_text = text[period:] + text[:period]\n",
        "        return np.mean([text[i] == shifted_text[i] for i in range(len(text))])\n",
        "\n",
        "    kappa_values = []\n",
        "    for period in range(1, max_period + 1):\n",
        "        kappa_values.append(kappa(ciphertext, period))\n",
        "    return max(kappa_values)\n",
        "\n",
        "# Digraphic Index of Coincidence\n",
        "def digraphic_index_of_coincidence(text):\n",
        "    pairs = [text[i:i+2] for i in range(len(text) - 1)]\n",
        "    freq = Counter(pairs)\n",
        "    total = sum(freq.values())\n",
        "    return sum(count * (count - 1) for count in freq.values()) / (total * (total - 1))\n",
        "\n",
        "# DIC for even-numbered pairs\n",
        "def dic_for_even_numbered_pairs(text):\n",
        "    pairs = [text[i:i+2] for i in range(0, len(text) - 1, 2)]\n",
        "    freq = Counter(pairs)\n",
        "    total = sum(freq.values())\n",
        "    return sum(count * (count - 1) for count in freq.values()) / (total * (total - 1))\n",
        "\n",
        "# Long repeat\n",
        "def long_repeat(text):\n",
        "    repeats = [text[i:i+3] for i in range(len(text) - 2)]\n",
        "    freq = Counter(repeats)\n",
        "    long_repeats = [repeat for repeat, count in freq.items() if count > 1]\n",
        "    return np.sqrt(len(long_repeats) / len(repeats)) if repeats else 0\n",
        "\n",
        "# Percentage of odd-spaced repeats\n",
        "def percentage_of_odd_spaced_repeats(text):\n",
        "    repeats = [text[i:i+3] for i in range(len(text) - 2)]\n",
        "    odd_spaced_repeats = [repeat for i, repeat in enumerate(repeats) if repeat in repeats[:i]]\n",
        "    return len(odd_spaced_repeats) / len(repeats) * 100 if repeats else 0\n",
        "\n",
        "# Log digraph score (example placeholder data)\n",
        "def log_digraph_score(text, log_digraph_scores):\n",
        "    pairs = [text[i:i+2] for i in range(len(text) - 1)]\n",
        "    total_score = sum(log_digraph_scores.get(pair, 0) for pair in pairs)\n",
        "    return total_score / len(pairs) if pairs else 0\n",
        "\n",
        "# Single letter-digraph discrepancy score (example placeholder data)\n",
        "def single_letter_digraph_discrepancy(text, single_letter_scores, digraph_scores):\n",
        "    single_scores = [single_letter_scores.get(char, 0) for char in text]\n",
        "    digraph_scores_list = [digraph_scores.get(text[i:i+2], 0) for i in range(len(text) - 1)]\n",
        "    return np.mean(single_scores) + np.mean(digraph_scores_list) if text else 0\n",
        "\n",
        "# Define log digraph scores and single letter scores (example data)\n",
        "log_digraph_scores = {'ex': 0.1, 'xa': 0.05}  # Example data\n",
        "single_letter_scores = {'e': 0.2, 'x': 0.1}  # Example data\n",
        "digraph_scores = {'ex': 0.15, 'xa': 0.1}  # Example data\n",
        "\n",
        "# Extract features from multiple files\n",
        "def extract_features_from_files(file_list):\n",
        "    all_features = []\n",
        "    for filename in file_list:\n",
        "        df = load_ciphertexts_from_csv(filename)\n",
        "        for index, row in df.iterrows():\n",
        "            ciphertext = row['Ciphertext']  # Adjust column name if necessary\n",
        "            features = {\n",
        "                'Algorithm': row['Algorithm'],  # Include algorithm for reference\n",
        "                'Number of Unique Characters': len(set(ciphertext)),\n",
        "                'Chi-Squared Statistic': chi_squared_statistic(frequency_distribution(ciphertext)),\n",
        "                'Index of Coincidence': index_of_coincidence(ciphertext),\n",
        "                'Max IC for Periods 1-15': max_ic_for_periods(ciphertext),\n",
        "                'Max Kappa for Periods 1-15': max_kappa_for_periods(ciphertext),\n",
        "                'Digraphic Index of Coincidence': digraphic_index_of_coincidence(ciphertext),\n",
        "                'DIC for Even-Numbered Pairs': dic_for_even_numbered_pairs(ciphertext),\n",
        "                'Long Repeat': long_repeat(ciphertext),\n",
        "                'Percentage of Odd-Spaced Repeats': percentage_of_odd_spaced_repeats(ciphertext),\n",
        "                'Log Digraph Score': log_digraph_score(ciphertext, log_digraph_scores),\n",
        "                'Single Letter-Digraph Discrepancy Score': single_letter_digraph_discrepancy(ciphertext, single_letter_scores, digraph_scores)\n",
        "            }\n",
        "            all_features.append(features)\n",
        "\n",
        "    # Convert features to DataFrame and save to CSV\n",
        "    features_df = pd.DataFrame(all_features)\n",
        "    features_df.to_csv('ciphertext_features_combined.csv', index=False)\n",
        "    print(\"Feature extraction complete. Results saved to 'ciphertext_features_combined.csv'\")\n",
        "\n",
        "# List of files\n",
        "file_list = [f'testing_group_{i}.csv' for i in range(1, 10)]\n",
        "\n",
        "# Extract features from all files\n",
        "extract_features_from_files(file_list)\n"
      ],
      "metadata": {
        "id": "tVJQBaaVXGUA",
        "outputId": "748dae8e-ef80-4a11-eacf-26600cad2e74",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 14,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Feature extraction complete. Results saved to 'ciphertext_features_combined.csv'\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import pandas as pd\n",
        "import joblib\n",
        "\n",
        "# Load the features from the CSV file\n",
        "features_df = pd.read_csv('ciphertext_features_combined.csv')\n",
        "\n",
        "# Load the trained SVM model\n",
        "svm_classifier = joblib.load('rf_classifier_model.joblib')\n",
        "\n",
        "# Define the mapping from class labels to algorithm names\n",
        "# Update this mapping based on how your model was trained\n",
        "class_labels = {\n",
        "    0: 'aes',\n",
        "    1: 'des',\n",
        "    2: 'blowfish',\n",
        "    3: 'rc4',\n",
        "    4: '3des'\n",
        "}\n",
        "\n",
        "# Ensure that the columns in the test set match the training set\n",
        "# Drop any columns that are not used during training\n",
        "columns_to_keep = ['Number of Unique Characters', 'Chi-Squared Statistic', 'Index of Coincidence',\n",
        "                    'Max IC for Periods 1-15', 'Max Kappa for Periods 1-15', 'Digraphic Index of Coincidence',\n",
        "                    'DIC for Even-Numbered Pairs', 'Long Repeat', 'Percentage of Odd-Spaced Repeats',\n",
        "                    'Log Digraph Score', 'Single Letter-Digraph Discrepancy Score']\n",
        "features_df = features_df[columns_to_keep]\n",
        "\n",
        "# Check if there are any missing columns in the test set compared to the training set\n",
        "missing_cols = [col for col in columns_to_keep if col not in features_df.columns]\n",
        "if missing_cols:\n",
        "    raise ValueError(f\"Missing columns in test data: {', '.join(missing_cols)}\")\n",
        "\n",
        "# Predict the class labels for the testing data\n",
        "predictions = svm_classifier.predict(features_df)\n",
        "\n",
        "# Map the predictions to algorithm names\n",
        "predicted_algorithm_names = [class_labels[label] for label in predictions]\n",
        "\n",
        "# Add predictions and algorithm names to the features DataFrame\n",
        "features_df['Predicted_Label'] = predictions\n",
        "features_df['Predicted_Algorithm'] = predicted_algorithm_names\n",
        "\n",
        "# Save the results to a new CSV file\n",
        "features_df.to_csv('ciphertext_predictions.csv', index=False)\n",
        "\n",
        "print(\"Predictions and algorithm names have been saved to 'ciphertext_predictions.csv'\")\n"
      ],
      "metadata": {
        "id": "E1QdjtmjbPB4",
        "outputId": "d4c150f2-6434-455f-c89b-4779a9ee1aa6",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 23,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Predictions and algorithm names have been saved to 'ciphertext_predictions.csv'\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "from Crypto.Cipher import DES, ARC4\n",
        "from Crypto.Random import get_random_bytes\n",
        "from Crypto.Util.Padding import pad, unpad\n",
        "\n",
        "# Function to generate DES ciphertext\n",
        "def generate_des_ciphertext(plaintext, key):\n",
        "    # Ensure the key is 8 bytes (64 bits) for DES\n",
        "    if len(key) != 8:\n",
        "        raise ValueError(\"DES key must be 8 bytes long.\")\n",
        "\n",
        "    des = DES.new(key, DES.MODE_ECB)\n",
        "    padded_plaintext = pad(plaintext, DES.block_size)\n",
        "    ciphertext = des.encrypt(padded_plaintext)\n",
        "    return ciphertext\n",
        "\n",
        "\n",
        "\n",
        "# Example usage\n",
        "if __name__ == \"__main__\":\n",
        "    # Sample plaintext\n",
        "    plaintext = b'This is a secret message.'\n",
        "\n",
        "    # Generate random keys for DES and RC4\n",
        "    des_key = get_random_bytes(8)  # DES requires 8-byte key\n",
        "\n",
        "    # Generate DES ciphertext\n",
        "    des_ciphertext = generate_des_ciphertext(plaintext, des_key)\n",
        "    print(f\"DES Ciphertext (hex): {des_ciphertext.hex()}\")\n",
        "\n",
        "\n",
        "    des = DES.new(des_key, DES.MODE_ECB)\n",
        "    des_decrypted = unpad(des.decrypt(des_ciphertext), DES.block_size)\n",
        "    print(f\"DES Decrypted: {des_decrypted.decode()}\")\n",
        "\n",
        ""
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "rNfN_kfUnEby",
        "outputId": "6d6c8271-4e13-4801-f76b-862b237c390f"
      },
      "execution_count": 32,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "DES Ciphertext (hex): 95eeee249ceee76a4ec0d5396ba215cadc1b58015f6f62670b3db70c62b1a830\n",
            "DES Decrypted: This is a secret message.\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n",
        "import joblib\n",
        "\n",
        "# Load the trained model\n",
        "svm_classifier = joblib.load('svm_classifier_model.joblib')\n",
        "\n",
        "# Define class labels (these should match the labels used during training)\n",
        "class_labels = ['AES', 'DES', 'Blowfish', 'RC4', '3DES']  # Replace with actual class labels if different\n",
        "\n",
        "# Define feature extraction functions (omitting for brevity; assume they are defined)\n",
        "\n",
        "# Define a function to classify a ciphertext\n",
        "def classify_ciphertext(ciphertext):\n",
        "    features = {\n",
        "        'Number of Unique Characters': len(set(ciphertext)),\n",
        "        'Chi-Squared Statistic': chi_squared_statistic(frequency_distribution(ciphertext)),\n",
        "        'Index of Coincidence': index_of_coincidence(ciphertext),\n",
        "        'Max IC for Periods 1-15': max_ic_for_periods(ciphertext),\n",
        "        'Max Kappa for Periods 1-15': max_kappa_for_periods(ciphertext),\n",
        "        'Digraphic Index of Coincidence': digraphic_index_of_coincidence(ciphertext),\n",
        "        'DIC for Even-Numbered Pairs': dic_for_even_numbered_pairs(ciphertext),\n",
        "        'Long Repeat': long_repeat(ciphertext),\n",
        "        'Percentage of Odd-Spaced Repeats': percentage_of_odd_spaced_repeats(ciphertext),\n",
        "        'Log Digraph Score': log_digraph_score(ciphertext, log_digraph_scores),\n",
        "        'Single Letter-Digraph Discrepancy Score': single_letter_digraph_discrepancy(ciphertext, single_letter_scores, digraph_scores)\n",
        "    }\n",
        "\n",
        "    features_df = pd.DataFrame([features])\n",
        "\n",
        "    # Predict using the classifier\n",
        "    prediction_index = svm_classifier.predict(features_df)[0]\n",
        "\n",
        "    # Map the prediction index to the class label\n",
        "    predicted_algorithm = class_labels[prediction_index]\n",
        "\n",
        "    return predicted_algorithm\n",
        "\n",
        "# Input function to get ciphertext from user\n",
        "def input_and_classify():\n",
        "    ciphertext = input(\"Enter the ciphertext to classify: \")\n",
        "    predicted_algorithm = classify_ciphertext(ciphertext)\n",
        "    print(f\"The predicted algorithm for the given ciphertext is: {predicted_algorithm}\")\n",
        "\n",
        "# Call the input and classify function\n",
        "input_and_classify()\n"
      ],
      "metadata": {
        "id": "52gz8y0Cb6zx",
        "outputId": "b94821e2-e7a7-46cf-b446-bea42a974c21",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "execution_count": 33,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Enter the ciphertext to classify: 95eeee249ceee76a4ec0d5396ba215cadc1b58015f6f62670b3db70c62b1a830\n",
            "The predicted algorithm for the given ciphertext is: DES\n"
          ]
        }
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": [],
      "gpuType": "V28"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "accelerator": "TPU"
  },
  "nbformat": 4,
  "nbformat_minor": 0
}